/*----------------------------------------------------------------------------

	Copyright (c) 2006, Creator of the Cant Project.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	+ Redistributions of source code must retain the above copyright notice,
	this list of conditions and the following disclaimer.

	+ Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

	+ Neither the name of the Creator of the Cant Project nor the names of its
	contributors may be used to endorse or promote products derived from this
	software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

----------------------------------------------------------------------------*/

#ifndef	_PROJTAG_HPP_
#define	_PROJTAG_HPP_

#include	<map>
#include	<string>
#include	<vector>

#include	"depend.hpp"
#include	"except.hpp"
#include	"project.hpp"
#include	"types.hpp"
#include	"../util/log.hpp"
#include	"../util/stringiter.hpp"
#include	"tiny/tinyxml.h"



class Project;
class Tag;



/**
	A project tag.
	
	The base class for project-specific tags.
	
	
	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class ProjectTag
{
	public:
		/**
			Typedef to simplify use of the map of attributes.
		
		*/
		typedef	std::map<std::string,std::string>	typeAttributeMap;


		/**
			Typedef to simplify use of the vector of tags.
		
		*/
		typedef	std::vector<Tag*>				typeTagList;
	
	
	private:
		/**
			Tag name.

		*/
		std::string							m_strType;

		
		/**
			Map of attributes.

		*/
		std::map<std::string,std::string>	m_mapAttributes;
		
		
		/**
			List of nested tags.
			
		*/
		std::vector<Tag*>					m_vectorTags;
		
		
		/**
			Line number of containing build file.
			
			Used for error messages and diagnostics.
			
			@see		getLineNumber()
		*/
		int									m_iLineNumber;


	public:
		/**
			Default constructor.
			
			Required for STL containers.
		*/
		ProjectTag(void );
		
		
		/**
			Constructor.

		*/
		ProjectTag(TiXmlElement* );
		
		
		/**
			Copy constructor.
			
			Required for STL containers.
		*/
		ProjectTag(const ProjectTag& );
		
		
		/**
			Destructor.
			
		*/
		virtual	~ProjectTag();
		
		
		/**
			Assignment operator.
			
			Required for STL containers.
		*/
		ProjectTag&	operator=(const ProjectTag& );
		
		
		/**
			Gives the name of the tag.
			
		*/
		std::string	getType(void ) const;
		
		
		/**
			Gives a value for a named attribute.
			
		*/
		std::string	getAttribute(const std::string& );
		
		
		/**
			Gives a map of all attributes.
			
		*/
		typeAttributeMap&	getAttributes(void );
		
		
		/**
			Gives a list of all nested tags.
			
		*/
		typeTagList&	getTags(void );
		
		
		/**
			Gives the line number of the containing tag.
			
		*/
		int	getLineNumber(void ) const;

		
		/**
			Indexer to give a value for a named attribute.
			
		*/
		std::string	operator[](const std::string& );


	protected:
		/**
			Copies a <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copy(TiXmlElement* );
		
		
		/**
			Copies an <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copy(const ProjectTag& );
		
		
		/**
			Indicates whether a given name is an acceptable type to be nested.
			
			By default, returns <code>true</code>. Derived classes may
			override to generate to strictly type nested elements. If this
			method returns <code>false</code> an error will be generated
			by the caller, indicating to the parser unit that there has been
			a tag error.
			
			@return		<code>true</code>.
		*/
		virtual bool	isAcceptableTag(const std::string& a_strTagType ) const;
		
		
	private:
		/**
			Removes all existing nested tags.
			
			Each <code>Tag</code> pointer is deleted and the list cleared.
		*/
		void	deleteTags(void );
		
		
		/**
			Builds a structure of nested tags.
			
		*/
		void	storeNestedElements(TiXmlElement* );
};



//--------------------------------------------------------------------------//



/**
	An <code>XML</code> include tag.
	
	
	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class ProjectInclude	: public ProjectTag
{
	private:
		/**
			Constant.
			
		*/
		static const std::string			STR_ATTRIB_PATH;
		static const std::string			STR_NAME_FILETAG;
		
		
	private:
		/**
			File-path attribute.
			
		*/
		std::string					m_strPath;
		
		
	public:
		/**
			Constructor.

		*/
		ProjectInclude(TiXmlElement* );
		
		
		/**
			Copy constructor.
			
			Required for STL containers.
		*/
		ProjectInclude(const ProjectInclude& );
		
		
		/**
			Assignment operator.
			
			Required for STL containers.
		*/
		ProjectInclude&	operator=(const ProjectInclude& );
		
		
		/**
			Gives the value of the <code>path</code> attribute.
			
		*/
		std::string	getPath(void );


	protected:
		/**
			Copies a <code>ProjectTag</code>.

			Used by copy constructor and assignment operator.
		*/
		void	copyInclude(TiXmlElement* );


		/**
			Copies an <code>ProjectTag</code>.

			Used by copy constructor and assignment operator.
		*/
		void	copyInclude(const ProjectInclude& );
	
		
		/**
			Indicates whether a named tag can be nested within this tag.
			
			Currently, only allows the <code>File</code> tag to be nested.
			
			@return		<code>true</code> if the named tag can nested,
						<code>false</code> otherwise.
		*/
		bool	isAcceptableTag(const std::string& a_strTagType ) const;
};



//--------------------------------------------------------------------------//



/**
	An <code>XML</code> property tag.
	
	
	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class ProjectProperty	: public ProjectTag
{
	private:
		/**
			Constant.
			
		*/
		static const std::string		STR_ATTRIB_NAME;
		static const std::string		STR_ATTRIB_VALUE;

	
	private:
		/**
			Property name.
			
			@see		getName
		*/
		std::string					m_strName;
		
		
		/**
			Property value.
			
			@see		getValue
		*/
		std::string					m_strValue;
		
		
	public:
		/**
			Default constructor.
			
			Required for STL containers.
		*/
		ProjectProperty(void );
		
		
		/**
			Constructor.

		*/
		ProjectProperty(TiXmlElement* );
		
		
		/**
			Copy constructor.
			
			Required for STL containers.
		*/
		ProjectProperty(const ProjectProperty& );
		
		
		/**
			Assignment operator.
			
			Required for STL containers.
		*/
		ProjectProperty&	operator=(const ProjectProperty& );
		
		
		/**
			Gives the property name.
			
		*/
		std::string	getName(void ) const;
		
		
		/**
			Gives the property value.
			
		*/
		std::string	getValue(void ) const;
		
		
	protected:
		/**
			Copies a <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copyProperty(TiXmlElement* );
		
		
		/**
			Copies an <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copyProperty(const ProjectProperty& );
};



//--------------------------------------------------------------------------//


/**
	An <code>XML</code> target tag.
	
	
	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class ProjectTarget	: public ProjectTag
{
	private:
		/**
			Constant.
			
		*/
		static const std::string		STR_ATTRIB_NAME;
		static const std::string		STR_ATTRIB_DEPENDS;
		static const std::string		STR_ATTRIB_DESCRIPTION;
	
	
	private:
		/**
			Target name.
			
		*/
		std::string					m_strName;
		
		
		/**
			Target dependendencies.
			
		*/
		Dependency					m_depends;
		
		
		/**
			Target description.
		
		*/
		std::string					m_strDescription;
	
	
	public:
		/**
			Default constructor.
			
			Required for STL containers.
		*/
		ProjectTarget(void );
		
		
		/**
			Constructor.

		*/
		ProjectTarget(TiXmlElement* );
		
		
		/**
			Copy constructor.
			
			Required for STL containers.
		*/
		ProjectTarget(const ProjectTarget& );
		
		
		/**
			Assignment operator.
			
			Required for STL containers.
		*/
		ProjectTarget&	operator=(const ProjectTarget& );
		
		
		/**
			Gives the target name.
			
		*/
		std::string	getName(void ) const;
		
		
		/**
			Gives the target dependencies.
			
		*/
		Dependency&	getDepends(void );
		
		
		/**
			Gives the target description
			
		*/
		std::string	getDescription(void ) const;
		
		
		/**
			Executes this tag.
			
			@param		a_project			invoking project.
		*/
		void	execute(Project& a_project );
	
	
	protected:
		/**
			Internal <code>execute</code> method.
			
			A <code>Dependency</code> object is passed as an argument. This
			allows a running list of called targets to be maintained. In this
			way, no target will be called more than once, no matter how many
			times it appears in the dependency chain.
			
			@param		a_project			invoking project.
			@param		a_dependencies		list of dependencies called.
		*/
		void	execute(Project& a_project, Dependency& a_dependencies );
		
		
		/**
			Copies a <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copyTarget(TiXmlElement* );
		
		
		/**
			Copies an <code>ProjectTag</code>.
			
			Used by copy constructor and assignment operator.
		*/
		void	copyTarget(const ProjectTarget& );
};



//--------------------------------------------------------------------------//



/**
	Base class for user-defined tags.


	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class Tag	: public ProjectTag
{
	public:
		/**
			Default constructor.
			
			Required for STL containers.
		*/
		Tag(void );
		
		
		/**
			Constructor.

		*/
		Tag(TiXmlElement* );
		
		
		/**
			Copy constructor.
			
			Required for STL containers.
		*/
		Tag(const Tag& );
		
		
		/**
			Destructor.
			
		*/
		virtual	~Tag();
		
		
		/**
			Assignment operator.
			
			Required for STL containers.
		*/
		Tag&	operator=(const Tag& );


		/**
			Gives an object of identical type and state.
			
		*/
		virtual Tag*	clone(void ) const = 0x0;
		
		
		/**
			Executes this tag.
			
			@param		a_project		containing project.
			@param		a_target		containing target.
		*/
		virtual void	execute(Project& a_project, ProjectTarget& a_target ) = 0x0;
};



//--------------------------------------------------------------------------//



/**
	Interface that <code>Tag</code> creators must implement.
	

	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class ITagCreator
{
	public:
		/**
			Destructor.
			
		*/
		virtual	~ITagCreator();
		
		
		/**
			Creates a typed <code>Tag</code> object.
			
			@param		a_pElement		xml element.
			@param		a_strName		name of required tag.
			@return		typed <code>Tag</code> object if name is recognised,
						<code>null</code> otherwise.
		*/
		virtual Tag*	createTag(TiXmlElement* a_pElement, const std::string& a_strName ) = 0x0;
};



//--------------------------------------------------------------------------//



/**
	Factory class responsible for generating typed <code>Tag</code> objects
	for a given type.


	@version	0.1
	@author		<a href="mailto:cant.project@gmail.com">Mikey</a>
*/
class TagFactory	: public ITagCreator
{
	public:
		/**
			Typedef to simplify using list of creators.
			
		*/
		typedef	vector<ITagCreator*>	typeCreatorList;
		
		
	private:
		/**
			List of registered <code>ITagCreator</code> objects.
			
			The list will be traversed first to last.
		*/
		vector<ITagCreator*>			m_vectorCreators;
		
		
		/**
			Hidden constructor.
			
		*/
		TagFactory(void );
		
		
	public:
		/**
			Destructor.
			
		*/
		~TagFactory();
		
		
		/**
			Singleton method.
			
			@return		single instance of this class.
		*/
		inline static TagFactory&	getInstance(void )
		{
			static TagFactory				instance;

			return	instance;
		}
		
		
		/**
			Registers an <code>ITagCreator</code> object.
			
			@param		a_creator		object to be registered.
		*/
		void	registerCreator(ITagCreator* a_pcreator );
		
		
		/**
			Creates a typed <code>Tag</code> object.
			
			Implementation for the <code>ITagCreator</code> interface.
			
			@param		a_pElement		xml element.
			@param		a_strName		name of required tag.
			@return		typed <code>Tag</code> object if name is recognised,
						<code>null</code> otherwise.
		*/
		Tag*	createTag(TiXmlElement* a_pElement, const std::string& a_strName );
};

#endif
